﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LotroData
{
    /// <summary>
    /// Represents a read-only collection of keys and values.
    /// </summary>
    /// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
    /// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
    public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        private IDictionary<TKey, TValue> Backing;

        /// <summary>
        /// Initialises a new instance of the <see cref="ReadOnlyDictionary{TKey, TValue}"/> class
        /// that is a read-only wrapper around the specified dictionary.
        /// </summary>
        /// <param name="backing">The <see cref="IDictionary{TKey, TValue}"/> to wrap.</param>
        public ReadOnlyDictionary(IDictionary<TKey, TValue> backing)
        {
            Backing = backing;
        }

        /// <summary>
        /// Adds an item with the provided key and value to the
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </summary>
        /// <param name="key">The object to use as the key of the element to add.</param>
        /// <param name="value">The object to use as the value of the element to add.</param>
        /// <exception cref="NotSupportedException">
        /// Always. This dictionary is read-only.
        /// </exception>
        public void Add(TKey key, TValue value)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Determines whether the <see cref="ReadOnlyDictionary{TKey, TValue}"/> contains an
        /// element with the specified key.
        /// </summary>
        /// <param name="key">The key to locate.</param>
        /// <returns><see lang="true"/> if the <see cref="ReadOnlyDictionary{TKey, TValue}"/>
        /// contains an element with the key; otherwise, <see lang="false"/>.</returns>
        public bool ContainsKey(TKey key)
        {
            return Backing.ContainsKey(key);
        }

        /// <summary>
        /// Gets an <see cref="ICollection{TKey}"/> containing the keys of the
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </summary>
        /// <value>An <see cref="ICollection{TKey}"/> containing the keys of the
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.</value>
        public ICollection<TKey> Keys
        {
            get { return Backing.Keys; }
        }

        /// <summary>
        /// Removes the element with the specified key from the
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <returns>
        /// <see lang="true"/> if the element is successfully removed; otherwise,
        /// <see lang="false"/>. This method also returns <see lang="false"/> if
        /// <paramref name="key"/> was not found in the original
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </returns>
        /// <exception cref="NotSupportedException">
        /// Always. This dictionary is read-only.
        /// </exception>
        public bool Remove(TKey key)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key whose value to get.</param>
        /// <param name="value">
        /// When this method returns, the value associated with the specified key, if the key is
        /// found; otherwise, the default value for the type of the <paramref name="value"/>
        /// parameter. This parameter is passed uninitialised.
        /// </param>
        /// <returns><see lang="true"/> if the <see cref="ReadOnlyDictionary{TKey, TValue}"/>
        /// contains an element with the specified key; otherwise, <see lang="false"/>.</returns>
        public bool TryGetValue(TKey key, out TValue value)
        {
            return Backing.TryGetValue(key, out value);
        }

        /// <summary>
        /// Gets an <see cref="ICollection{TValue}"/> containing the values in the
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </summary>
        /// <value>An <see cref="ICollection{TValue}"/> containing the values in the
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.</value>
        public ICollection<TValue> Values
        {
            get { return Backing.Values; }
        }

        /// <summary>
        /// Gets or sets the element with the specified key.
        /// </summary>
        /// <param name="key">The key of the element to get or set.</param>
        /// <value>The element with the specified key.</value>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="key"/> is <see lang="null"/>.
        /// </exception>
        /// <exception cref="KeyNotFoundException">
        /// The property is retrieved and <paramref name="key"/> is not found.
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// The property is set. This dictionary is read-only.
        /// </exception>
        public TValue this[TKey key]
        {
            get
            {
                return Backing[key];
            }
            set
            {
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// Adds an item to the <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </summary>
        /// <param name="item">
        /// The object to add to the <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </param>
        /// <exception cref="NotSupportedException">
        /// Always. This dictionary is read-only.
        /// </exception>
        public void Add(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Removes all items from the <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </summary>
        /// <exception cref="NotSupportedException">
        /// Always. This dictionary is read-only.
        /// </exception>
        public void Clear()
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Determines whether the <see cref="ReadOnlyDictionary{TKey, TValue}"/> contains a
        /// specified element.
        /// </summary>
        /// <param name="item">
        /// The object to locate in the <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </param>
        /// <returns>
        /// <see lang="true"/> if <paramref name="item"/> is found in the
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>; otherwise, <see lang="false"/>.
        /// </returns>
        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return Backing.Contains(item);
        }

        /// <summary>
        /// Copies the elements of the <see cref="ReadOnlyDictionary{TKey, TValue}"/> to an
        /// <see cref="Array"/>, starting at a particular <see cref="Array"/> index.
        /// </summary>
        /// <param name="array">
        /// The one-dimensional <see cref="Array"/> that is the destination of the elements copied
        /// from <see cref="ReadOnlyDictionary{TKey, TValue}"/>. The <see cref="Array"/> must have
        /// zero-based indexing.
        /// </param>
        /// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which
        /// copying begins.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="array"/> is <see lang="null"/>.
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less
        /// than 0.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="array"/> is multi-dimensional.<br/>
        /// -or-<br/>
        /// <paramref name="arrayIndex"/> is equal to or greater than the length of
        /// <paramref name="array"/>.<br/>
        /// -or-<br/>
        /// The number of elements in the <see cref="ReadOnlyDictionary{TKey, TValue}"/> is greater
        /// than the available space from <paramref name="arrayIndex"/> to the end of the
        /// destination <paramref name="array"/>.<br/>
        /// </exception>
        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            Backing.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Gets the number of elements contained in the
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </summary>
        /// <value>
        /// The number of elements contained in the <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </value>
        public int Count
        {
            get { return Backing.Count; }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="ReadOnlyDictionary{TKey, TValue}"/>
        /// is read-only.
        /// </summary>
        /// <value><see lang="true"/> always. This dictionary is read-only.</value>
        public bool IsReadOnly
        {
            get { return true; }
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </summary>
        /// <param name="item">
        /// The object to remove from the <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </param>
        /// <returns>
        /// <see lang="true"/> if the element is successfully removed; otherwise,
        /// <see lang="false"/>. This method also returns <see lang="false"/> if
        /// <paramref name="key"/> was not found in the original
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.
        /// </returns>
        /// <exception cref="NotSupportedException">
        /// Always. This dictionary is read-only.
        /// </exception>
        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>An enumerator that can be used to iterate through the
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.</returns>
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return Backing.GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>A enumerator that can be used to iterate through the
        /// <see cref="ReadOnlyDictionary{TKey, TValue}"/>.</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return ((System.Collections.IEnumerable)Backing).GetEnumerator();
        }
    }
}
